In [ ]:
from PIL import Image, ImageEnhance
import numpy as np
import matplotlib.pyplot as plt
import cv2
import os
import pandas as pd
import math
In [ ]:
def approx_contours(contours, epsilon_factor=0.02):
approximated_contours = []
for contour in contours:
epsilon = epsilon_factor * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
approximated_contours.append(approx)
return approximated_contours
def remove_nested_rectangles(rectangles):
non_nested = []
for rect in rectangles:
x1, y1, w1, h1 = rect
nested = False
for other_rect in rectangles:
if other_rect == rect:
continue
x2, y2, w2, h2 = other_rect
if x1 >= x2 and y1 >= y2 and x1 + w1 <= x2 + w2 and y1 + h1 <= y2 + h2:
nested = True
break
if not nested:
non_nested.append(rect)
return non_nested
def merge_rectangles(rectangles, distance_threshold):
merged = []
while rectangles:
a = rectangles.pop(0)
to_merge = [a]
i = 0
while i < len(rectangles):
b = rectangles[i]
if is_close(a, b, distance_threshold):
to_merge.append(b)
rectangles.pop(i)
else:
i += 1
xs = [x for x, _, w, _ in to_merge for x in [x, x+w]]
ys = [y for _, y, _, h in to_merge for y in [y, y+h]]
merged.append((min(xs), min(ys), max(xs) - min(xs), max(ys) - min(ys)))
return merged
def is_close(rect1, rect2, threshold):
"""
Check if two rectangles are close to each other based on a threshold.
"""
x1, y1, w1, h1 = rect1
x2, y2, w2, h2 = rect2
center1 = (x1 + w1 / 2, y1 + h1 / 2)
center2 = (x2 + w2 / 2, y2 + h2 / 2)
distance = np.hypot(center1[0] - center2[0], center1[1] - center2[1])
return distance < threshold
def merge_contours(contours, threshold=10):
"""
Merge contours that are close to each other.
"""
bounding_rects = [cv2.boundingRect(c) for c in contours]
merged_contours = []
for i, (x1, y1, w1, h1) in enumerate(bounding_rects):
if not any(i in c for c in merged_contours):
merged = [i]
for j, (x2, y2, w2, h2) in enumerate(bounding_rects):
if i != j and (abs(x1 - x2) <= threshold and abs(y1 - y2) <= threshold):
merged.append(j)
merged_contours.append(merged)
# Combine the contours in each group
final_contours = []
for group in merged_contours:
merged_points = [contours[i] for i in group]
merged_contour = np.vstack(merged_points)
final_contours.append(merged_contour)
return final_contours
def apply_contour_symmetry(contour, image_shape):
# Calculate the bounding box of the contour
x, y, w, h = cv2.boundingRect(contour)
# Only proceed if the contour is more than a single point
if contour.shape[0] > 1:
# Create a blank canvas for the symmetric image
symmetric_image = np.zeros(image_shape, dtype=np.uint8)
# Adjust contour to a local coordinate system centered at the bounding box
adjusted_contour = contour - [x, y]
# Reflect contour across the vertical axis
reflected_contour_vertical = np.array([[w - p[0][0], p[0][1]] for p in adjusted_contour])
reflected_contour_vertical += [x, y] # Adjust back to global coordinates
# Reflect contour across the horizontal axis
reflected_contour_horizontal = np.array([[p[0][0], h - p[0][1]] for p in adjusted_contour])
reflected_contour_horizontal += [x, y] # Adjust back to global coordinates
# Draw the original, vertically reflected, and horizontally reflected contours
cv2.drawContours(symmetric_image, [contour, reflected_contour_vertical, reflected_contour_horizontal], -1, (255), thickness=cv2.FILLED)
return symmetric_image
else:
# If the contour is a single point, simply return an empty canvas
return np.zeros(image_shape, dtype=np.uint8)
def smooth_contours(contours, alpha=0.02):
# alpha parameter controls the approximation accuracy
smoothed_contours = [cv2.approxPolyDP(cnt, alpha * cv2.arcLength(cnt, True), True) for cnt in contours]
return smoothed_contours
In [ ]:
directory_path = os.getcwd()
parent_directory_path = os.path.dirname(directory_path)
csv_path = os.path.join(parent_directory_path, 'Model\\condo_data_new_FINAL_test.csv')
gt_masked_image_path = os.path.join(parent_directory_path, 'Model\\buildings\\test')
generated_image_path = os.path.join(parent_directory_path, 'Model\\buildings\\final_buildings_output_3')
# Read the CSV file
data = pd.read_csv(csv_path)
# Function to extract the numeric part of the filename
def extract_numeric_part(filename):
numeric_part = ''.join(filter(str.isdigit, filename))
return int(numeric_part) if numeric_part else None
def create_binary_mask(arr, target_color, threshold=30):
lower_bound = np.array(target_color) - threshold
upper_bound = np.array(target_color) + threshold
mask = (arr[:, :, :3] >= lower_bound) & (arr[:, :, :3] <= upper_bound)
return np.all(mask, axis=-1)
def extract_building_regions(arr, target_color, threshold=10):
lower_bound = np.array(target_color) - threshold
upper_bound = np.array(target_color) + threshold
mask = (arr[:, :, :3] >= lower_bound) & (arr[:, :, :3] <= upper_bound)
return np.all(mask, axis=-1)
# def find_max_building_storeys(gpr):
# max_building_storeys= 0
# if gpr >= 0 and gpr < 1.4:
# max_building_storeys = 5
# elif gpr >= 1.4 and gpr < 1.6:
# max_building_storeys = 12
# elif gpr >= 1.6 and gpr < 2.1:
# max_building_storeys = 24
# elif gpr >= 2.1 and gpr < 2.8:
# max_building_storeys = 36
# elif gpr >= 2.8:
# max_building_storeys = 48 ## by right got no limit
# return max_building_storeys
def masked_rgb(simp_gpr):
rgb = [0,0,0]
if simp_gpr == 1.4:
rgb = [255, 10, 169]
elif simp_gpr == 1.6:
rgb = [200,130,60]
elif simp_gpr == 2.1:
rgb = [0,0,255]
elif simp_gpr == 2.8:
rgb = [255,0,0]
elif simp_gpr == 3.0:
rgb =[0,0,0]
return rgb
'''
pink, [255, 10, 169]
brown, [200,130,60]
cyan, [0,255,255]
red, [255,0,0]
black, [0,0,0]
green, [0,255,0]
blue, [0,0,255]
yellow, [255, 255, 0]
'''
# absolute_accuracies = []
# losses =[]
# images =[]
# sanity_ratios =[]
gprs =[]
generated_gprs =[]
sanity_ratios =[]
# Iterate through the images in the generated_image_path
for image_file in os.listdir(generated_image_path):
if image_file.endswith('.png'):
image_index = extract_numeric_part(image_file)
# Construct the path for the corresponding masked image
gt_mask_image_filename = f"{image_index}.png"
gt_mask_image = os.path.join(gt_masked_image_path, gt_mask_image_filename)
open_gt_mask_image = Image.open(gt_mask_image)
mask_crop_box = (512, 0, 1024, 512) # right side
mask_image = open_gt_mask_image.crop(mask_crop_box) #gt_mask is concatenated gt and mask
gt_crop_box = (0, 0, 512, 512) # left side
gt_image = open_gt_mask_image.crop(gt_crop_box)
generated_image = os.path.join(generated_image_path, image_file)
generated_image = Image.open(generated_image)
# Check if the image index matches any index in the CSV
matched_row = data[data['key1'] == image_index]
if not matched_row.empty:
# Extract the GPR value for the matched row
gpr_value = matched_row['GPR'].iloc[0]
storey = matched_row['storeys'].iloc[0]
simplified_gpr_value = matched_row['simp_gpr'].iloc[0]
actual_site_area = matched_row['area'].iloc[0]
actual_site_area = actual_site_area.replace(',', '')
actual_site_area = float(actual_site_area[:-4])
gpr_value = float(gpr_value)
storey = int(storey)
mask_array = np.array(mask_image)
generated_array = np.array(generated_image)
mask_color = [0,255,0] # green
site_mask = create_binary_mask(mask_array, mask_color)
site_area_array = generated_array.copy()
site_area_array[~site_mask] = [255, 255, 255, 255] # making non-masked region white RMB ITS 4 CHANNELS NOW
site_area_image = Image.fromarray(site_area_array)
mask_color = masked_rgb(simplified_gpr_value)
building_mask = extract_building_regions(site_area_array, mask_color)
buildings_image = Image.fromarray(building_mask)
building_pixels = np.sum(building_mask)
mask_pixels = np.sum(site_mask)
msq_per_pixel = actual_site_area/mask_pixels
building_area = msq_per_pixel * building_pixels
#max_storeys = find_max_building_storeys(gpr_value)
generated_gpr = building_area*storey/actual_site_area
gprs.append(gpr_value)
generated_gprs.append(generated_gpr)
print(f'Image: {image_file}, GPR: {gpr_value}, Simplified GPR: {simplified_gpr_value}, Storeys:{storey}, Site area: {actual_site_area}, Building pixels: {building_pixels}, Mask pixels: {mask_pixels}, Generated GPR: {generated_gpr}')
# get contours
building_mask = building_mask.astype(np.uint8) * 255
blurred = cv2.GaussianBlur(building_mask, (5, 5), 0) # Apply GaussianBlur to reduce noise and improve edge detection
edges = cv2.Canny(blurred, 50, 150) #Canny edge detection
contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
approximated_contours = approx_contours(contours)
original_approximated_contours = cv2.drawContours(building_mask.copy(), approximated_contours, -1, (255), 10)
blank_image_for_contour = np.zeros_like(building_mask)
approximated_contours_image = cv2.drawContours(blank_image_for_contour, approximated_contours, -1, (255), 5)
#get rectangles
blank_image_for_rectangle = np.zeros_like(building_mask)
rectangles = [cv2.boundingRect(contour) for contour in approximated_contours]
non_nested_rectangles = remove_nested_rectangles(rectangles)
distance_threshold = 10
merged_rectangles = merge_rectangles(non_nested_rectangles, distance_threshold)
min_width = 10
min_height = 10
for x, y, w, h in merged_rectangles:
if w >= min_width and h >= min_height:
cv2.rectangle(blank_image_for_rectangle, (x, y), (x + w, y + h), (255), -1) # Fill the rectangle
# different attempt
# kernel = np.ones((3,3), np.uint8)
# dialated_image = cv2.dilate(approximated_contours_image, kernel, iterations=2)
# contours, _ = cv2.findContours(dialated_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# symmetric_contours_image = np.zeros_like(building_mask)
# cv2.drawContours(symmetric_contour_image, contours, -1, (255), thickness=cv2.FILLED)
# merged_contours = merge_contours(contours)
# for contour in merged_contours:
# symmetric_contour_image = apply_contour_symmetry(contour, building_mask.shape)
# symmetric_contours_image = cv2.bitwise_or(symmetric_contours_image, symmetric_contour_image)
kernel = np.ones((3,3), np.uint8)
opened_image = cv2.morphologyEx(approximated_contours_image, cv2.MORPH_OPEN, kernel, iterations=1)
closed_image = cv2.morphologyEx(opened_image, cv2.MORPH_CLOSE, kernel, iterations=1)
contours, _ = cv2.findContours(closed_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contour_pixels = sum(cv2.contourArea(contour) for contour in contours)
min_area_threshold = 0.1*contour_pixels
large_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area_threshold] #discard small contours
smoothed_contours = smooth_contours(large_contours)
smoothed_image = np.zeros_like(approximated_contours_image)
cv2.drawContours(smoothed_image, smoothed_contours, -1, (255), thickness=cv2.FILLED)
for i, c in enumerate(large_contours):
area = cv2.contourArea(c)
print(f"Large contour {i} area: {area}")
plt.figure(figsize=(35, 5))
plt.subplot(1, 7, 1)
plt.imshow(mask_image)
plt.title('Mask Image')
plt.axis('off')
plt.subplot(1, 7, 2)
plt.imshow(gt_image)
plt.title('GT Image')
plt.axis('off')
plt.subplot(1, 7, 3)
plt.imshow(generated_image)
plt.title('Generated Image')
plt.axis('off')
plt.subplot(1, 7, 4)
plt.imshow(buildings_image, cmap='gray')
plt.title('Buildings Image')
plt.axis('off')
plt.subplot(1, 7, 5)
plt.imshow(approximated_contours_image, cmap='gray')
plt.title('Buildings Image Contour')
plt.axis('off')
plt.subplot(1, 7, 7)
plt.imshow(blank_image_for_rectangle, cmap='gray')
plt.title('Buildings Image Rectangle')
plt.axis('off')
plt.subplot(1, 7, 6)
plt.imshow(smoothed_image, cmap='gray')
plt.title('Buildings Image Smoothed')
plt.axis('off')
plt.show()
total_data = len(gprs)
accuracies = []
absolute_error =[]
square_error =[]
for tar_gpr, gen_gpr in zip(gprs, generated_gprs):
accuracies.append(abs((tar_gpr-gen_gpr)/tar_gpr))
absolute_error.append(abs(tar_gpr-gen_gpr))
square_error.append((tar_gpr-gen_gpr)**2)
accuracy = sum(accuracies)/total_data
mean_abs_error = sum(absolute_error)/total_data
root_squared_error = math.sqrt(sum(square_error)/total_data)
print(f"Accuracies:{accuracies} \nSquare error:{square_error} \nAbsolute error:{absolute_error} ")
print(f"\nAccuracy:{accuracy} MAE:{mean_abs_error} RMSE:{root_squared_error}")
Image: 1040.png, GPR: 1.4, Simplified GPR: 1.4, Storeys:5, Site area: 23065.1, Building pixels: 3790, Mask pixels: 16203, Generated GPR: 1.1695365055853852 Large contour 0 area: 2073.0 Large contour 1 area: 746.0 Large contour 2 area: 1845.5 Large contour 3 area: 1447.5
Image: 1074.png, GPR: 2.5, Simplified GPR: 2.8, Storeys:12, Site area: 37265.0, Building pixels: 3180, Mask pixels: 27439, Generated GPR: 1.3907212361966543 Large contour 0 area: 2976.0 Large contour 1 area: 1907.0
Image: 1076.png, GPR: 2.8, Simplified GPR: 2.8, Storeys:36, Site area: 10414.2, Building pixels: 276, Mask pixels: 8554, Generated GPR: 1.1615618424129064 Large contour 0 area: 502.5
Image: 1102.png, GPR: 1.6, Simplified GPR: 1.6, Storeys:12, Site area: 6157.3, Building pixels: 135, Mask pixels: 4778, Generated GPR: 0.33905399748848886 Large contour 0 area: 1174.5
Image: 1180.png, GPR: 3.0, Simplified GPR: 3.0, Storeys:15, Site area: 19547.0, Building pixels: 908, Mask pixels: 14355, Generated GPR: 0.9487983281086729 Large contour 0 area: 1567.5 Large contour 1 area: 698.5
Image: 1379.png, GPR: 1.4, Simplified GPR: 1.4, Storeys:5, Site area: 17455.9, Building pixels: 4008, Mask pixels: 12216, Generated GPR: 1.6404715127701375 Large contour 0 area: 1151.5 Large contour 1 area: 5102.0
Image: 145.png, GPR: 2.8, Simplified GPR: 2.8, Storeys:15, Site area: 22094.4, Building pixels: 1643, Mask pixels: 16292, Generated GPR: 1.5127056223913578 Large contour 0 area: 2530.0
Image: 1484.png, GPR: 3.0, Simplified GPR: 3.0, Storeys:17, Site area: 10097.1, Building pixels: 655, Mask pixels: 7670, Generated GPR: 1.4517601043024773 Large contour 0 area: 670.5 Large contour 1 area: 924.0 Large contour 2 area: 200.0
Image: 1602.png, GPR: 3.0, Simplified GPR: 3.0, Storeys:17, Site area: 13564.8, Building pixels: 1389, Mask pixels: 9962, Generated GPR: 2.370307167235495 Large contour 0 area: 589.5 Large contour 1 area: 1043.5 Large contour 2 area: 1103.5
Image: 1655.png, GPR: 2.1, Simplified GPR: 2.1, Storeys:18, Site area: 27418.2, Building pixels: 1991, Mask pixels: 21829, Generated GPR: 1.641760960190572 Large contour 0 area: 1329.5 Large contour 1 area: 894.5 Large contour 2 area: 878.5
Image: 1670.png, GPR: 2.8, Simplified GPR: 2.8, Storeys:13, Site area: 17940.2, Building pixels: 1870, Mask pixels: 11806, Generated GPR: 2.059122480094867 Large contour 0 area: 2880.0
Image: 1796.png, GPR: 2.8, Simplified GPR: 2.8, Storeys:17, Site area: 13877.2, Building pixels: 1365, Mask pixels: 9365, Generated GPR: 2.4778430325680723 Large contour 0 area: 1907.0
Image: 1811.png, GPR: 1.4, Simplified GPR: 1.4, Storeys:5, Site area: 7255.7, Building pixels: 1287, Mask pixels: 5237, Generated GPR: 1.2287569219018522 Large contour 0 area: 1819.5 Large contour 1 area: 770.5
Image: 1876.png, GPR: 2.1, Simplified GPR: 2.1, Storeys:19, Site area: 10502.8, Building pixels: 1251, Mask pixels: 8276, Generated GPR: 2.872039632672789 Large contour 0 area: 304.0 Large contour 1 area: 1935.5
Image: 191.png, GPR: 3.5, Simplified GPR: 3.0, Storeys:18, Site area: 13000.3, Building pixels: 1724, Mask pixels: 9208, Generated GPR: 3.370112945264987 Large contour 0 area: 3352.0
Image: 2000.png, GPR: 3.0, Simplified GPR: 3.0, Storeys:17, Site area: 13241.8, Building pixels: 1276, Mask pixels: 9680, Generated GPR: 2.2409090909090907 Large contour 0 area: 3300.5
Image: 434.png, GPR: 2.1, Simplified GPR: 2.1, Storeys:16, Site area: 39401.6, Building pixels: 2232, Mask pixels: 28711, Generated GPR: 1.2438438229250113 Large contour 0 area: 1260.5 Large contour 1 area: 1017.5 Large contour 2 area: 1144.0
Image: 489.png, GPR: 2.1, Simplified GPR: 2.1, Storeys:15, Site area: 28692.65, Building pixels: 2027, Mask pixels: 20526, Generated GPR: 1.4812920198772288 Large contour 0 area: 1528.0 Large contour 1 area: 917.5 Large contour 2 area: 1480.0
Image: 491.png, GPR: 3.0, Simplified GPR: 3.0, Storeys:16, Site area: 18747.8, Building pixels: 1752, Mask pixels: 13058, Generated GPR: 2.146729973962322 Large contour 0 area: 3024.5
Image: 568.png, GPR: 3.4, Simplified GPR: 3.0, Storeys:19, Site area: 14344.0, Building pixels: 1084, Mask pixels: 10510, Generated GPR: 1.9596574690770696 Large contour 0 area: 2071.0
Accuracies:[0.16461678172472477, 0.44371150552133826, 0.5851564848525334, 0.7880912515696944, 0.6837338906304424, 0.171765366264384, 0.45974799200308647, 0.5160799652325075, 0.2098976109215016, 0.2182090665759181, 0.2645991142518332, 0.115056059797117, 0.12231648435581984, 0.36763792032037557, 0.03711058706714659, 0.2530303030303031, 0.4076934176547566, 0.2946228476775101, 0.2844233420125593, 0.42363015615380306] Square error:[0.053113422257795126, 1.2304993758250788, 2.6844795962373893, 1.5899848212497598, 4.207428298769776, 0.05782654845395843, 1.657126814622821, 2.397046774629476, 0.3965130636349866, 0.2099830176054666, 0.5488994995007807, 0.10378511166493604, 0.029324191796528332, 0.5960451944175346, 0.016870646987736278, 0.576219008264463, 0.7330033995436596, 0.38279956466759957, 0.7280697373343397, 2.0745866063854725] Absolute error:[0.23046349441461467, 1.1092787638033457, 1.6384381575870934, 1.2609460025115111, 2.0512016718913273, 0.24047151277013756, 1.287294377608642, 1.5482398956975227, 0.6296928327645048, 0.45823903980942804, 0.740877519905133, 0.32215696743192757, 0.17124307809814776, 0.7720396326727887, 0.12988705473501305, 0.7590909090909093, 0.8561561770749888, 0.6187079801227713, 0.853270026037678, 1.4403425309229303] Accuracy:0.3405565073808677 MAE:0.8559018812475208 RMSE:1.0068168824033883
In [ ]: